package com.eulog.client.blackJack;

import com.eulog.client.blackJack.BlackJackStrategy.SD;
import com.eulog.client.trump.TrumpCard;

public class BlackJackProbability {
	
	private final BlackJackDecks decks;
	private final BlackJackRules rules;
	private final TrumpCard dealerFaceCard;
	private final BlackJackHandCards playerCards;
	
	private double strategyProbability[];
	private double basicProbabilityByDealerFaceCard[];
	
	private double dealerCardProbability[];
	
	private double winningEdgeWhenStay;
	private double winningEdgeWhenDouble;
	private double winningEdgeWhenHit;
	
	/*
	 * Full Counting Properties
	 */
	private int cardStatistic[];
	private double fullCountingProbabilityByDealerFaceCard[];
	private double fullCountingDealerCardProbability[];
	private double fullCountingWinningEdgeWhenStay;
	private double fullCountingWinningEdgeWhenDouble;
	private double fullCountingWinningEdgeWhenHit;
	
	public BlackJackProbability(BlackJackStrategy strategy, BlackJackHandCards _playerCards,TrumpCard _dealerFaceCard, int countingStrategyIndex) {
		
		decks = strategy.getDecks();
		rules = strategy.getRules();
		dealerFaceCard = _dealerFaceCard;
		playerCards = _playerCards;
		
		strategyProbability = calculateCountingProbabilitiesOfEachCard(strategy.getCardStatistic(), strategy.getCountingStarategyByIndex(countingStrategyIndex));		
		basicProbabilityByDealerFaceCard = getBasicProbabilityOfEachCardWithAdditionalCardsAtDecks(new BlackJackHandCards(dealerFaceCard),false,true);
	
		dealerCardProbability = getCountingStrategyDealerPossibility(false);
		
		winningEdgeWhenStay = getWinningEdgeAtCertainNumber(playerCards.calculateRanks(), 1,false,false);
		winningEdgeWhenDouble = getCountingStrategyPlayerWinningEdgeWhenHit(true,false,false);
		winningEdgeWhenHit = getCountingStrategyPlayerWinningEdgeWhenHit(false,true,false);

		/*
		 * Full Counting Calculation
		 */
		cardStatistic = strategy.getCardStatistic();
		fullCountingProbabilityByDealerFaceCard = getBasicProbabilityOfEachCardWithAdditionalCardsAtDecks(new BlackJackHandCards(dealerFaceCard),true,true); 
		fullCountingDealerCardProbability = getCountingStrategyDealerPossibility(true);
		fullCountingWinningEdgeWhenStay = getWinningEdgeAtCertainNumber(playerCards.calculateRanks(),1,true,false);
		fullCountingWinningEdgeWhenDouble=getCountingStrategyPlayerWinningEdgeWhenHit(true,false,true);
		fullCountingWinningEdgeWhenHit=getCountingStrategyPlayerWinningEdgeWhenHit(false,true,true);
	}

	public BlackJackStrategy.SD getDecision() {
		if( (winningEdgeWhenStay > winningEdgeWhenHit) && (winningEdgeWhenStay > winningEdgeWhenDouble) ) return SD.S;
		if( (winningEdgeWhenHit > winningEdgeWhenStay) && (winningEdgeWhenHit > winningEdgeWhenDouble) ) return SD.H;
		
		//When Double
		if((winningEdgeWhenStay > winningEdgeWhenHit)) return SD.X;
		return SD.D; 
	}
	
	public BlackJackStrategy.SD getFullCountDecision() {
		if( (fullCountingWinningEdgeWhenStay > fullCountingWinningEdgeWhenHit) && (fullCountingWinningEdgeWhenStay > fullCountingWinningEdgeWhenDouble) ) return SD.S;
		if( (fullCountingWinningEdgeWhenHit > fullCountingWinningEdgeWhenStay) && (fullCountingWinningEdgeWhenHit > fullCountingWinningEdgeWhenDouble) ) return SD.H;
		
		//When Double
		if((fullCountingWinningEdgeWhenStay > fullCountingWinningEdgeWhenHit)) return SD.X;
		return SD.D; 
	}
	
	public double getWinningEdgeWhenStay() {return winningEdgeWhenStay;}
	public double getWinningEdgeWhenDouble() {return winningEdgeWhenDouble;}
	public double getWinningEdgeWhenHit() {return winningEdgeWhenHit;}
	
	public double getFullCountWinningEdgeWhenStay() {return fullCountingWinningEdgeWhenStay;}
	public double getFullCountWinningEdgeWhenDouble() {return fullCountingWinningEdgeWhenDouble;}
	public double getFullCountWinningEdgeWhenHit() {return fullCountingWinningEdgeWhenHit;}
	
	private double[] calculateCountingProbabilitiesOfEachCard(int[] cardStatistic,int countingStrategy[]) {
		int remainingCardNo = decks.getTotalCards() - decks.getUsedCardsNumber();
		double probability[] = new double[10];
		double counting = 0;
		double negativeAlpha = 0;
		double positiveAlpha = 0;
	
		// initialize counting, and alpha
		for(int i=0; i<10; i++) {
			if(countingStrategy[i]<0) negativeAlpha += countingStrategy[i];
			else if(countingStrategy[i]>0) positiveAlpha += countingStrategy[i];
			
			counting += countingStrategy[i]*cardStatistic[i];
		}
		
		// calculate probabilities of each card
		int index;
		double alpha = 0;
		for(int i=2; i<12; i++) {
			index = BlackJackStrategy.getIndexOfCardByEvaluation(i);
			
			if(countingStrategy[index] < 0) alpha = -1 * negativeAlpha;
			else if(countingStrategy[index] > 0) alpha = positiveAlpha;
			
			if(alpha != 0 && counting !=0) probability[index] = ( countingStrategy[index] * counting ) / (remainingCardNo*alpha);
		}
		
		return probability;
	}
	
	private double[] getBasicProbabilityOfEachCardWithAdditionalCardsAtDecks(BlackJackHandCards dealerCards,boolean fullCountFlag,boolean isDealer) {
		double probability[] = new double[10];
		double remainingCard;

		int index; 
		int cardCount;
		int cardCountTenValue = decks.getTotalCardSizeByRank(true);
		int cardCountNotTenValue = decks.getTotalCardSizeByRank(false);
		int totalCards = decks.getTotalCards();
		boolean ignoreTenValue=false;
		boolean ignoreAceValue=false;
		
		if(isDealer && rules.doesDealerPeeks() && (dealerCards.size()==1) ) {
			if(dealerFaceCard.isAce()) {
				totalCards -= cardCountTenValue;
				ignoreTenValue=true;
			}
			else if(BlackJack.evaluateRank(dealerFaceCard)==10) {
				totalCards -= cardCountNotTenValue;
				ignoreAceValue=true;
			}
		}

		
		for(int i=2; i<12; i++) {
			index = BlackJackStrategy.getIndexOfCardByEvaluation(i);
			
			if(i==10) cardCount = cardCountTenValue;
			else cardCount = cardCountNotTenValue;

			if(fullCountFlag) {
				cardCount -= cardStatistic[index];
				remainingCard = totalCards - decks.getUsedCardsNumber();
			}
			else {
				
				remainingCard = totalCards;
			}
			
			if( (ignoreAceValue && (i==11) )==false && (ignoreTenValue && (i==10) )==false) {
				probability[index] = (cardCount - dealerCards.cardSizeByEvaluation(i))/(remainingCard);
			}
			else {
				probability[index] = 0;
			}
		}
		
		double norm=0;
		for(int i=0; i<10; i++) norm+=probability[i];
		for(int i=0; i<10; i++) probability[i]/=norm;
		
//test
if(ignoreAceValue==true || ignoreTenValue==true) {
	norm=0;
	for(int i=0; i<10; i++) norm+=probability[i];
	ignoreAceValue=true;
}

		return probability;
	}

	private double[] getCountingStrategyDealerPossibility(boolean fullcountingFlag) {
		double dealerProbability[] = new double[23]; // blackjack[0],1~21,bust[22]
		for (int i=0; i<23; i++) {
			dealerProbability[i] = 0;
		}
		
		BlackJackHandCards tempHandCards = new BlackJackHandCards();
		tempHandCards.add(dealerFaceCard);
		
		calculateDealerCardProbability(tempHandCards,dealerProbability,1,fullcountingFlag);

		return dealerProbability;
	}
	
	private void calculateDealerCardProbability(
			BlackJackHandCards dealerCards,
			double dealerProbability[], // OUT 
			double weight,
			boolean fullCountFlag) {

		BlackJackHandCards tempHandCards = new BlackJackHandCards();
		int eval;
		int index;
		double prob;
		double basicProbability[] = getBasicProbabilityOfEachCardWithAdditionalCardsAtDecks(dealerCards,fullCountFlag,true);
		
		dealerProbability[dealerCards.calculateRanks()] = 0;
		
		for(int i=2; i<12; i++) {
			tempHandCards.clear();
			tempHandCards.add(dealerCards);
			tempHandCards.add(BlackJack.getCardByEvaluation(i));
			
			eval = tempHandCards.calculateRanks();
			index = BlackJackStrategy.getIndexOfCardByEvaluation(i);
			if(eval>21) eval = 22;
			else if(tempHandCards.isBlackJack()) eval = 0;
			
			
			if(fullCountFlag) prob = basicProbability[index]*weight;
			else prob = (strategyProbability[index]+basicProbability[index])*weight;
			
			dealerProbability[eval] += prob;
			
			if( (eval>0 && eval<17) || (rules.doesDealerHitOnSoft17() && (tempHandCards.calculateRanksAllAceAreOneValue()<17) )) {
				calculateDealerCardProbability(tempHandCards, dealerProbability, dealerProbability[eval],fullCountFlag);
			}
		}
		
	}

	private double getWinningEdgeAtCertainNumber(int playerEvaluation,double weight,boolean fullCountFlag,boolean doublingDown) {
		if(weight==0) return 0;
		
		double edge = 0;
		double cardProb[];
			
		if(fullCountFlag) cardProb = fullCountingDealerCardProbability;
		else cardProb = dealerCardProbability;
		
		if(playerEvaluation <22) {
			for(int i=17; i<22; i++) {
				if(playerEvaluation<i) edge -= cardProb[i]; 
				else if(playerEvaluation>i) edge += cardProb[i];
			}
			edge += cardProb[22];
		}
		else {
			edge -= (1-cardProb[0]); 
		}
			
			
		if(doublingDown) edge*=2;
		edge -= cardProb[0];
		
		return edge * weight;
		
	}
	
	private double getCountingStrategyPlayerWinningEdgeWhenHit(boolean doublingDown,boolean recursive,boolean fullCountFlag) {
		
		double playerCardProbability[] = new double[23]; // blackjack[0],1~21,bust[22]
		for (int i=0; i<23; i++) {
			playerCardProbability[i] = 0;
		}
		
		calculatePlayerCardProbability(new BlackJackHandCards(),playerCardProbability,1,recursive,fullCountFlag,doublingDown);

////test playerCardProbability
//double testresult = 0;
//for (int i=0; i<23; i++) {
//	testresult += playerCardProbability[i];
//}
		
		double prob=0;		
		// copy double probability to hit probability
		for (int i=2; i<23; i++) {
			prob += getWinningEdgeAtCertainNumber(i,playerCardProbability[i],fullCountFlag,doublingDown);
		}

		return prob;
	}
	
	private void calculatePlayerCardProbability(
			BlackJackHandCards virtuallyAdditionalHandCards,
			double playerCardProbability[], // OUT
			double weight,
			boolean recursive,
			boolean fullCountFlag,
			boolean doublingDown) {

		int eval;
		int index;
		BlackJackHandCards tempHandCards = new BlackJackHandCards();
		double basicProbability[];
		
		// For recursive
		double tempCardProbability[] = new double[23]; // blackjack[0],1~21,bust[22]
		double tempStay;
		double tempHit;
		

		//if(virtuallyAdditionalHandCards.size()>0) {
			tempHandCards.add(playerCards);
			tempHandCards.add(virtuallyAdditionalHandCards);
			tempHandCards.add(dealerFaceCard);
			basicProbability = getBasicProbabilityOfEachCardWithAdditionalCardsAtDecks(virtuallyAdditionalHandCards,fullCountFlag,false);
		//}
//		else {
//			if(fullCountFlag) basicProbability = fullCountingProbabilityByDealerFaceCard; 
//			else basicProbability = basicProbabilityByDealerFaceCard; 	
//		}
		
		
		tempHandCards.clear();
		tempHandCards.add(playerCards);
		tempHandCards.add(virtuallyAdditionalHandCards);
		playerCardProbability[tempHandCards.calculateRanks()] = 0;
		
		// Calculate Probability
		for(int i=2; i<12; i++) {
			tempHandCards.clear();
			tempHandCards.add(playerCards);
			tempHandCards.add(virtuallyAdditionalHandCards);
			tempHandCards.add(BlackJack.getCardByEvaluation(i));
			
			eval = tempHandCards.calculateRanks();
			index = BlackJackStrategy.getIndexOfCardByEvaluation(i);
			if(eval>21) eval = 22;
			
			if(fullCountFlag) playerCardProbability[eval] += (basicProbability[index])*weight;
			else playerCardProbability[eval] += (strategyProbability[index]+basicProbability[index])*weight;			
		
			if(eval < 17 && recursive) {
				tempHandCards.clear();
				tempHandCards.add(virtuallyAdditionalHandCards);
				tempHandCards.add(BlackJack.getCardByEvaluation(i));
				
				for (int j=0; j<23; j++) {
					tempCardProbability[j] = 0;
				}
				
				tempStay = getWinningEdgeAtCertainNumber(eval,1,fullCountFlag,doublingDown);
				calculatePlayerCardProbability(tempHandCards,tempCardProbability,1,false,fullCountFlag,doublingDown);
				tempHit = 0;
				for (int j=0; j<23; j++) {
					tempHit += getWinningEdgeAtCertainNumber(j,tempCardProbability[j],fullCountFlag,doublingDown);
				}
				
				if(tempHit > tempStay) {
					calculatePlayerCardProbability(tempHandCards,playerCardProbability,playerCardProbability[eval],true,fullCountFlag,doublingDown);
				}
			}
		}
	}
 
}
